/*进阶
Middleware中间件，重点：next()，hook钩子。
Plugin插件，用来更新Schema的功能，而全局Plugin用于更新全部Schema的功能，所以全局plugin最好放在靠前的位置。
Validation验证器，验证存入数据库的数据

作者：_Dot大师兄
链接：https://www.jianshu.com/p/9d34af40e135
*/


 /**
 * sql命令的规范
 */
// $符号是mongodb中的占位符，也就是说当我们指定一个位置或匹配到一个元素之后，这个符号会自动指向平级的元素来进行操作
// 需要在连接数据库的options配置{useFindAndModify： false}，使用才不会包警告
// DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify` option set to false are deprecated.
// *** is not a constructor 使用Schema生成实例。但Schema是不能生成实例的，必须到Model才能生成实例
// mongoose.Types.ObjectId(_vendorId)  依赖于其他表时需要用到ObjectId
// hook钩子（pre，next）优先级大于自身回调
// populate用于处理关联表


const mongoose = require('mongoose')
const Schema = mongoose.Schema
const log = console.log
const err = console.error

// Plugin插件，用来更新Schema的功能
// 全局Plugin，更新全部Schema的功能，全局plugin最好放在靠前的位置
// 以下代码为所有操作打上时间戳
function TimePlugin(schema, options) {
    const str = 'save time'
    schema.pre('save', next => {
        console.time(str)
        next()
    })
    schema.post('save', next => {
        console.timeEnd(str)
    })
}
mongoose.plugin(TimePlugin)

const PostSchema = new Schema({
    title: {
        type: String,
        // Validation  验证器 验证存入数据库的数据
        // 验证器，validate是放在Schema下的字段用来做字段限制的
        validate: {
            validator: v => v.length < 10,
            message: '文章标题长度必须小于10'
        }
    },
    author: {
        type: Schema.Types.ObjectId,
        ref: 'User'
    },
    rawContent: String,
    create_at: {
        type: Date,
        default: Date.now
    },
    update_at: {
        type: Date,
        default: Date.now
    },
    comments: {
        type: Array
    },
    votes: Number,
    downs: Number
})

const UserSchema = new Schema({
    name: String,
    sex: Number,
    avatar: String,
    mail: String,
    password: String
})

const CommentSchema = new Schema({
    author: {
        type: Schema.Types.ObjectId,
        required: true
    },
    content: {
        type: 'string',
        required: true
    },
    postId: {
        type: Schema.Types.ObjectId,
        required: true
    }
})

const PostModel = mongoose.model('Post', PostSchema)
const UserModel = mongoose.model('User', UserSchema)
const CommentModel = mongoose.model('Comment', CommentSchema)

const db = mongoose.connect('mongodb://localhost/dolb').then(() => {

    // 1. 插入文章
    function addPost(post) {
        const postEntity = new PostModel(post)
        postEntity.save((e, result) => {
            if (e) {
                err(e)
            } else {
                log('保存成功啦')
                //    getPostByTitle('hello world')// 插入之后才能查到文章，而操作都是异步的，不能保证顺序，所以调用getPostByTitle放在addPost里能保证查得到
            }
        })
    }
    addPost({
        title: 'hello wo'
    })

    // 2. 根据id删除文章
    function removePostById(postId) {
        PostModel.remove({ _id: postId }, (e, res) => {
            if (e) {
                err(e)
            } else {
                log('删除所有id为...成功啦')
            }
        })
    }
    // removePostById('5a609fba3906973fc61f640b')

    // 3. 根据文章标题查找文章
    function getPostByTitle(title) {
        PostModel.find({ title }, (e, res) => {
            if (e) {
                err(e)
            } else {
                log(res)
            }
        })
    }

    // 4.更新文章
    function updatePost(id, title) {
        PostModel.update({ _id: id }, { title }, (e, res) => {
            if (e) {
                err(e)
            } else {
                log(res)
            }
        })
    }
    // updatePost('5a60a17d951bc340519c0e95','hey dolby')
})

// middleware中间件
PostSchema.pre('save', next => {
    log('hey i am pre hook')
    next()
})
PostSchema.post('save', next => {
    log('hey i am post hook')
})


/**
  `hey i am pre hook
  save time: 9.787ms
  hey i am post hook
  保存成功啦`
  ==>
  证明钩子优先级大于自身回调
 */
